// Copyright (C) 2008 Taylor L. Riche <riche@cs.utexas.edu>
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
//

#include "Monitor.h"

namespace lagniappe
{

std::string
mon_debug(Operator * o, ReqType rt)
{
	std::string ret;
	switch(rt) {
		case MORE_RESOURCES : ret = "MORE_RESOURCES"; break;
		case INIT_PROC : ret = "INIT_PROC"; break;
	}
	ret = o->getName() + "::" + ret;
	return ret;
}

std::string
mon_debug(Operator * o, RespType rt)
{
	std::string ret;
	switch(rt) {
		case ADD_PROC : ret = "ADD_PROC"; break;
		case REVOKE_PROC : ret = "REVOKE_PROC"; break;
		case RESOURCES_EXHAUSTED : ret = "RESOURCES_EXHAUSTED"; break;
		case WAIT : ret = "WAIT"; break;
	}
	ret = o->getName() + "::" + ret;
	return ret;
}

Monitor::Monitor(lagniappe::System * s) : sys(s)
{
	mutex = sys->getNewMutex();
	this->name = "monitor";
	outFile.open("adapt_log.dat", std::ios::out | std::ios::app);
	start = time(NULL);
	start += 5;
}

Monitor::~Monitor()
{
	outFile.close();
}

void
Monitor::processRequest(lagniappe::RCon * rcon)
{
	MonitorRequest * request = static_cast<MonitorRequest *>(rcon->getRData());
	Operator * reqOp = request->op;
	Operator * swapOp = NULL;

	// MON_DEBUG
	//std::cerr << mon_debug(this, request->msgType) << std::endl;

	MonitorResponse * resp = new MonitorResponse;
	MonitorResponse * swapResp = NULL;

	if(request->msgType == MORE_RESOURCES) {
		if((time(NULL) - opTimeMap[reqOp]) > 1) {
			ProcElement * tempProc = sys->getFreeProc(reqOp);
			//if(tempProc == NULL) tempProc = sys->roundRobin();
			if(tempProc != NULL) {
				sys->assignProc(tempProc, reqOp);
				resp->respBody.addProc.newProc = tempProc;
				resp->msgType = ADD_PROC;
				// MON_DEBUG
				//std::cerr << mon_debug(this, resp->msgType) << "::" << tempProc << std::endl;
				outFile << time(NULL) - start<< ":" << reqOp->getName() << ":" << resp->respBody.addProc.newProc << std::endl;
				opTimeMap[reqOp] = time(NULL);
			}
			else {
				System::SwapStruct swap = sys->findSwap(reqOp);
				//if(swap.foundSwap) {
				if(false) {
					resp->respBody.addProc.newProc = swap.targetProc;
					resp->msgType = ADD_PROC;
					// MON_DEBUG
					outFile << time(NULL) - start<< ":" << reqOp->getName() << ":" << resp->respBody.addProc.newProc << std::endl;
					std::cerr << mon_debug(this, resp->msgType) << "::" << swap.targetProc << std::endl;
					opTimeMap[reqOp] = time(NULL);
					swapResp = new MonitorResponse;
					swapResp->respBody.revokeProc.revokedProc = swap.targetProc;
					swapResp->msgType = REVOKE_PROC;
					swapOp = swap.oldOp;
					// MON_DEBUG
					outFile << time(NULL) - start << ":" << swapOp->getName() << "(R):" << swapResp->respBody.revokeProc.revokedProc << std::endl;
					std::cerr << mon_debug(this, swapResp->msgType) << "::" << swap.targetProc << std::endl;
				}
				else {
					resp->msgType = RESOURCES_EXHAUSTED;
					//std::cerr << mon_debug(this, resp->msgType) << std::endl;
				}
			}
			RCon * newRCon = new RCon(reqOp, static_cast<RData *>(resp), MONITOR_IN_PORT, sizeof(*resp));
			reqOp->run(newRCon);
			if(swapResp != NULL) {
				RCon * newSwapRCon = new RCon(swapOp, static_cast<RData *>(swapResp), MONITOR_IN_PORT, sizeof(*swapResp));
				swapOp->run(newSwapRCon);
			}
		}
		else {
			resp->msgType = WAIT;
			RCon * newRCon = new RCon(reqOp, static_cast<RData *>(resp), MONITOR_IN_PORT, sizeof(*resp));
			// MON_DEBUG
			//std::cerr << mon_debug(this, resp->msgType) << std::endl;
			reqOp->run(newRCon);
		}
	}
	else if(request->msgType == INIT_PROC) {
		//procMap[request->reqBody.initProc.proc].push_back(reqOp);
		//opCountMap[reqOp]++;
		sys->assignProc(request->reqBody.initProc.proc, reqOp);
		opTimeMap[reqOp] = time(NULL);
		outFile << time(NULL)  - start << ":" << reqOp->getName() << ":" << request->reqBody.initProc.proc << std::endl;
	}

	delete request;
	delete rcon;
}

void
Monitor::registerPorts(lagniappe::CommChannel * chan) {
	chan->registerQueue(MONITOR_IN_PORT);
}


}
